class Behaviour {

    width: number;
    height: number;

    private static allBehaviours: { [uuid: number]: Behaviour } = {};

    private static behaviourUuidIndex = 1;

    static getBehaviour(uuid: number) {
        return this.allBehaviours[uuid];
    }

    name: string;
    uuid = 0;

    constructor() {
        this.uuid = Behaviour.behaviourUuidIndex++;
        Behaviour.allBehaviours[this.uuid] = this;

    }


    $isDestroy = false;
    $isExecuteOnStart = false;

    gameObject: GameObject = null;

    onAddToGameObject() {

    }

    onStart() {

    }

    onUpdate(duringTime: number) {

    }

    onDestroy() {

    }
}

const SerializedField: (defaultValue?: any) => PropertyDecorator = (defaultValue: any) => {


    return (target, key) => {
        const clz = target.constructor;
        if (!clz['properties']) {
            clz['properties'] = [];
        }
        clz['properties'].push({ key: key, defaultValue: defaultValue });
    }
}



class Transform extends Behaviour {

    @SerializedField(0)
    x = 0;
    @SerializedField(0)
    y = 0;
    @SerializedField(1)
    scaleX = 1;
    @SerializedField(1)
    scaleY = 1;
    @SerializedField(0)
    rotation = 0;
    @SerializedField(1)
    alpha = 1;
    $globalAlpha = 1;
    @SerializedField(100)
    width = 100;
    @SerializedField(100)
    height = 100;



    /**
     * 相对矩阵
     */
    $localMatrix;

    /**
     * 绝对矩阵
     */
    $globalMatrix;

    constructor() {
        super();
        this.$localMatrix = new Matrix();
        this.$globalMatrix = new Matrix();
    }



    calculateGlobalProperties() {
        if (!this.gameObject.parent) {
            return;
        }
        this.y
        const parentTransform = this.gameObject.parent.getBehaviour(Transform);
        this.$localMatrix.updateFromDisplayObject(this.x, this.y, this.scaleX, this.scaleY, this.rotation);
        this.$globalMatrix = matrixAppendMatrix(this.$localMatrix, parentTransform.$globalMatrix);
        this.$globalAlpha = parentTransform.$globalAlpha * this.alpha;
    }

}

class TextRenderer extends Behaviour {

    @SerializedField("black")
    color: string;
    @SerializedField("helloworld")
    text: string;

    onAddToGameObject() {
        this.gameObject.renderer = this;
    }

    onDraw(context) {
        context.fillStyle = this.color;
        context.font = "30px 华文新魏";
        context.fillText(this.text, 0, 0);//MagicNumber
    }

    hitTest(point) {
        const rectangle = { x: 0, y: 0, width: this.width, htight: this.height };
        const result = checkPointInRectangle(point, rectangle);
        if (result) {
            return this.gameObject;
        }
        else {
            return null;
        }
    }
}


class RectRenderer extends Behaviour {


    @SerializedField('black')
    color: string
    transform;

    onAddToGameObject() {
        this.gameObject.renderer = this;
        this.transform = this.gameObject.getBehaviour(Transform);
    }

    onDraw(context) {


        context.beginPath();
        context.fillStyle = this.color;
        context.fillRect(0, 0, this.transform.width, this.transform.height);

    }

    hitTest(point) {
        const rectangle = { x: 0, y: 0, width: this.transform.width, height: this.transform.height };
        const result = checkPointInRectangle(point, rectangle);
        if (result) {
            console.log(this.gameObject.id + 'isclicked!');
            if (this.gameObject.collider) {

                const ps = this.gameObject.getBehaviour(Colliderer).position;
                // console.log("x:", this.transform.x, ",", "y:", this.transform.y, ",", "ps:", ps.x, ",", "py:", ps.y);
                console.log(this.gameObject.parent.id+"-"+this.gameObject.id);
            }
            return this.gameObject;
        }
        else {
            return null;
        }
    }
}


class ImageRenderer extends Behaviour {
    @SerializedField()
    image = null;
    transform
    texture

    onAddToGameObject() {
        this.gameObject.renderer = this;
        this.transform = this.gameObject.getBehaviour(Transform);

    }

    onDraw(context) {

        this.texture = getImage(this.image);

        if (this.texture) {
            context.drawImage(this.texture, 0, 0, this.transform.width, this.transform.height);
        }
    }


    hitTest(point) {
        const texture = getImage(this.image);

        if (!texture) {
            return null;
        }
        const rectangle = { x: 0, y: 0, width: this.transform.width, height: this.transform.height };
        let result = checkPointInRectangle(point, rectangle);
        if (result) {
            if (this.gameObject.collider) {

                const ps = this.gameObject.getBehaviour(Colliderer).position;
                // console.log("x:", this.transform.x, ",", "y:", this.transform.y, ",", "ps:", ps.x, ",", "py:", ps.y);
                console.log(this.gameObject.parent.id+"-"+this.gameObject.id);
            }
            return this.gameObject;
        }
        else {
            return null;
        }
    }



}

/**
 * 标签类 用于游戏中的识别判断
 */
class Tag extends Behaviour {

    @SerializedField(null)
    type: string;
    @SerializedField(0)
    grade: number;
    @SerializedField(0)
    situation:number;

    constructor(type,grade,situation)
    {
        super();
        this.type=type;
        this.grade=grade;
        this.situation=situation
    }

    onAddToGameObject() {
        this.gameObject.tag = this.type;
        this.gameObject.grade = this.grade;
        this.gameObject.situation=this.situation;
    }

}

class Box extends Behaviour { //box类，负责调整跳台类别，还需要优化。

    @SerializedField("normal")
    type: BoxType;

    private speed = 1;
    speedintex_box() {//不同跳台，角色不同的移动速度
        switch (this.type) {
            case BoxType.Speedup:
                this.speed = 2;
                break;
            case BoxType.Speeddown:
                this.speed = 0.5;
                break;
            default:
                this.speed = 1;
                break;
        }

        return this.speed;

    };


    get jumpintex_box() {
        switch (this.type) {
            case BoxType.Jumpup:
                return 1.25;
            default:
                return 1;
        }

    }



}

enum BoxType {

    //字符串用来序列化反序列化。
    Normal = "normal",
    Speedup = "speedup",
    Jumpup = "jumpup",
    Speeddown = "speeddown",


}

class Collider_Object {
    gameobject: GameObject = new GameObject();
    collider_direction: boolean[] = [false, false, false, false];
}

//AABB矩形碰撞盒
class Colliderer extends Behaviour {

    initFinish: boolean = false;

    countFrame: number;

    onStart() {

        this.init(this.gameObject);
        this.countFrame = 0;
        

    }

    position = { x: 0, y: 0 };
    size = { width: 0, height: 0 };
    $ColliderObj: Collider_Object[] = [];

    onAddToGameObject() {
        this.gameObject.collider = this;
        core.addcolliderObject(this.gameObject);
    }

    onUpdate() {

        this.init(this.gameObject);
        if (this.countFrame >= 3)
            this.initFinish = true;
        if (this.getBottom() < 0 || this.getTop() > 1200 || this.getRight() < 0 || this.getLeft() > 900) { }
        else if (this.gameObject.$isActive)
            core.addcolliderObject(this.gameObject);

        this.countFrame += 1;
    }

    // 子物体的世界坐标 = 它的transform.globalMatrix*(0,0)
    init(gameObject: GameObject) {
        const objectTransform = this.gameObject.getBehaviour(Transform);
        const point = new Point(0, 0);
        // const parent_map_transform=core.getObjectById('map');
        // this.position.x=gameObject.getBehaviour(Transform).x+parent_map_transform.x;
        // this.position.y=gameObject.getBehaviour(Transform).y+parent_map_transform.x;
        this.position.x = pointAppendMatrix(point, objectTransform.$globalMatrix).x;
         this.position.y = pointAppendMatrix(point, objectTransform.$globalMatrix).y;
        this.size.width = objectTransform.width;
        this.size.height = objectTransform.height;
    }

    //显示碰撞体盒子
    darwCollider(context) {
        context.beginPath();
        context.fillStyle = 'rgba(150, 150, 0, 0.5)';
        context.fillRect(0, 0, this.size.width, this.size.height);
    }

    onCollision() {
        if (this.initFinish) {
            this.$ColliderObj = [];
            for (let collider of core.$colliderObject) {
                //检测除自身外的所有的有碰撞体的对象。
                let isAdd = true;
                let gameObj: Collider_Object = new Collider_Object();
                if (collider.uuid != this.gameObject.uuid) {
                    gameObj.gameobject = this.detecteCollider(this.gameObject, collider).gameobject;
                    gameObj.collider_direction = this.detecteCollider(this.gameObject, collider).collider_direction;

                    if (gameObj.gameobject != null) {
                        //记录该对象碰撞到的其他碰撞体
                        for (let colliderObj of this.$ColliderObj) {
                            if (colliderObj.gameobject.uuid == gameObj.gameobject.uuid)
                                isAdd = false;
                            break;
                        }
                        if (gameObj.gameobject && isAdd) {

                            this.$ColliderObj.push(gameObj);

                        }
                    }
                }
            }

        }
        return this.$ColliderObj;
    }

    detecteCollider(gameObj1: GameObject, gameObj2: GameObject) {
        const collider1 = gameObj1.getBehaviour(Colliderer);
        const collider2 = gameObj2.getBehaviour(Colliderer);
        const merge_collider = new Collider_Object();
        merge_collider.gameobject = new GameObject();

        const l1 = collider1.getLeft();
        const t1 = collider1.getTop();
        const r1 = collider1.getRight();
        const b1 = collider1.getBottom();

        const l2 = collider2.getLeft();
        const t2 = collider2.getTop();
        const r2 = collider2.getRight();
        const b2 = collider2.getBottom();


        if (b1 < t2 || t1 > b2 || r1 < l2 || l1 > r2) {
            merge_collider.gameobject = null;
            return merge_collider;
        }
        else {
            if (b1 - t2 < this.size.height / 8) {
                merge_collider.collider_direction[1] = true;
                // console.log("下");
            }
            if (b2 - t1 < this.size.height / 8) {
                merge_collider.collider_direction[0] = true;
                // console.log("上");
            }
            if (r1 - l2 < this.size.width / 5) {
                merge_collider.collider_direction[3] = true;
                // console.log("右");
            }
            if (r2 - l1 < this.size.width / 5) {
                merge_collider.collider_direction[2] = true;
                // console.log("左");
            }
            merge_collider.gameobject = gameObj2;
            //console.log(gameObj1.id, "碰撞上了", gameObj2.id);
            return merge_collider;
        }
    }

    getTop() {
        return this.position.y;
    }
    getBottom() {
        return this.position.y + this.size.height;
    }
    getRight() {
        return this.position.x + this.size.width;
    }
    getLeft() {
        return this.position.x;
    }

}

class PlayerAnimation extends Behaviour {


    currentFrame: number;
    ImageRender: ImageRenderer;
    animationData;
    duringFrame;
    playerTransform;
    playFrame;
    FPS = 60;
    onStart() {
        this.currentFrame = 0;
        this.ImageRender = this.gameObject.getBehaviour(ImageRenderer);
        this.playerTransform = this.gameObject.getBehaviour(Transform);
    }


    playAnimation(animationType) {

        this.animationData = animation[animationType];
        this.duringFrame = this.FPS / this.animationData.length;
        const index = Math.trunc(this.currentFrame / this.duringFrame);
        this.playerTransform.width = getImage(this.animationData[index]).width;
        this.playerTransform.height = getImage(this.animationData[index]).height;
        this.ImageRender.image = this.animationData[index];

    }

    onUpdate() {
        
       
        if (this.currentFrame < this.FPS - 1)
            this.currentFrame++;
        else
            this.currentFrame = 0;

    }


}